#!/usr/bin/python
"""Contains classes representing zoqui core elements.
  
"""
__author__ = 'marco@zoqui.com (Marco Zoqui)'

import os
import sys
import cgi
import wsgiref.handlers
import datetime

from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.api import urlfetch
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from django.template import Context, Template
from django.utils import simplejson

#from jail import sessions

class ZString(str):
    def normalize(self):
        newstr=''
        lastch=''
        text=self.strip()
        for i in text:
            if ord(i)==32 or ord(i)==45 or ord(i)==95 or (ord(i)>=48 and ord(i)<=57) or (ord(i)>=65 and ord(i)<=90) or (ord(i)>=97 and ord(i)<=122):
                if not (i==' ' and lastch==' '):
                    newstr=newstr+i
                lastch=i
        return newstr
  
class YearMonthList():
    class Month():
        def __init__(self,id,desc,current):
            self.id = id
            self.desc = desc
            if (current==self.id):
                self.selected = 'selected'
            else:
                self.selected = ''
    class Year():
        def __init__(self,id,current):
            self.id = id
            if (current==self.id):
                self.selected = 'selected'
            else:
                self.selected = ''            
    
    def __init__(self,curyear=None,curmonth=None):
        self.years = []
        self.months = []
        if not curyear: 
            curyear = datetime.date.today().year
        if not curmonth:
            curmonth = datetime.date.today().month
        for i in range(2007,2021):
            self.years.append( YearMonthList.Year(i,curyear) )      
        for i in range(1,13):
            self.months.append( YearMonthList.Month(i,('Janeiro','Fevereiro','Marco','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro')[i-1],curmonth) )
   
class Month():
    def __init__(self,yyyy=None,mm=None):
        if not yyyy:
            yyyy=datetime.datetime.today().year
        if not mm:
            mm=datetime.datetime.today().month
        self.year = int(yyyy)
        self.month = int(mm)
        rawDate= "%04d-%02d-01T00:00:00" % (self.year, self.month)
        iDate = datetime.datetime.strptime(rawDate,'%Y-%m-%dT%H:%M:%S')
        eDate = datetime.datetime.strptime((iDate + datetime.timedelta(days=32)).strftime("%Y-%m-01"),"%Y-%m-%d")-datetime.timedelta(days=1) 
        self.iDate = iDate
        self.eDate = eDate + datetime.timedelta(seconds=86399)
    
    def description(self):
        txt=('Janeiro','Fevereiro','Marco','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro')[self.month-1]
        return ("%s %d" % (txt,self.year))

class TimePlus(datetime.time):
    def addMin(self,min):
        """Add N minutes and return a another time."""
        
        base = self
        return (datetime.datetime.strptime(base.strftime("%H%M"),"%H%M")+datetime.timedelta(minutes=min)).time()

    def difMin(self,min):
        """Substract N minutes and return a another time."""
        base = self
        return (datetime.datetime.strptime(base.strftime("%H%M"),"%H%M")-datetime.timedelta(minutes=min)).time()

    def getRange(ini, end):
        """Static function that retur a list of hours."""
        if (ini<0 or ini>24) or (end < 0 or end > 24) or (end < ini):
            return None
        items=[]
        for h in range(ini,end):
            items.append(('%02d:%02d' % (h,0)))
            items.append(('%02d:%02d' % (h,30)))
        return items
    getRange = staticmethod(getRange)

    
class Week():
    def __init__(self, dateRef = None):
        if dateRef:
            curdate = dateRef
        else:
            curdate = datetime.date.today()
        week = [None,None,None,None,None,None]
        if curdate.weekday()==6:
            curdate=curdate+datetime.timedelta(days=1)
        curwd   = curdate.weekday()
        if curwd>0:
            for iwd in range(0,curwd):
                week[iwd]=curdate-datetime.timedelta(days=curwd-iwd)
        week[curwd]=curdate
        if curwd<6:
            for iwd in range(curwd+1,6):
                week[iwd]=curdate+datetime.timedelta(days=iwd-curwd)
        week.insert(0,week[0]-datetime.timedelta(days=1))
        self.date=curdate
        self.days=week
        self.labels=['Domingo','Segunda','Terca','Quarta','Quinta','Sexta','Sabado']
        self.columns={'Domingo':week[0],'Segunda':week[1],'Terca':week[2],'Quarta':week[3],'Quinta':week[4],'Sexta':week[5],'Sabado':week[6]}

    def getDate(self,column):
        return self.columns[column]
    
    def getDateAsId(self,column):
        return self.getDate(column).strftime("%Y%m%d")


class DateBr():
    def __init__(self, rawdate):
        self.rawDate = rawdate
    def get(self):
        if type(self.rawDate)==str or type(self.rawDate)==unicode:
            if len(self.rawDate)==10: #dd/mm/yyyy
                return datetime.datetime.strptime(self.rawDate ,'%Y-%m-%d').date()
            elif len(self.rawDate)==8:
                if self.rawDate("/"): #dd/mm/yy
                    return datetime.datetime.strptime(self.rawDate ,'%y-%m-%d').date()
                else: #ddmmyyyy
                    return datetime.datetime.strptime(self.rawDate ,'%Y%m%d').date()
            elif len(self.rawDate)==6:#ddmmyy
                return datetime.datetime.strptime(self.rawDate ,'%y%m%d').date()
            return None
        elif type(self.rawDate)==datetime.datetime or type(self.rawDate)==datetime.date:
            return self.rawDate.strftime('%d-%m-%Y')
        else:
            return None
    def extenso(self):
        dt = self.rawDate
        if type(self.rawDate)==str or type(self.rawDate)==unicode:
            dt = self.get()
        return str(dt.day) + " de " + ('Janeiro','Fevereiro','Marco','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro')[dt.month-1] + " de " + str(dt.year)
        
class ModelBase(db.Model):
    def id(self):
        if self.is_saved():
            return self.key().id()
        return None
    
class LocalUser(db.Model):
    user = db.UserProperty()
    nome = db.StringProperty()
    tokens = db.StringListProperty()
    def domain(self):
        if self.user:
            email = self.user.email() 
            return email[email.find("@")+1:]
    def nick(self):
        if self.user:
            email = self.user.email() 
            return email[:email.find("@")]
            
class Menu:
    def __init__(self,caption,item,parent=None):
        """Defines a dropdown menu structure.
        
        caption - / for root node or string label
        item = dictionary for the menu items:
                    {label:url,label:url}
        """
        self.options= []
        self.caption = caption
        self.item = None
        self.data = None
        self.parent = parent
        if isinstance(item,dict):
            keys = item.keys()
            keys.sort()
            for key in keys:
                self.options.append(Menu(key,item[key],self))
        else:
            self.item = item
        
        if not parent:
            self.data = self.getHTML()
            
    def getHTML(self):
        html = ''
        if self.options.__len__()==0:
            html += ('\n<dd><a href="%s">%s</a></dd>' % (self.item,self.caption))
        else:
            if self.parent:
                html += '\n<dt>%s</dt>' % (self.caption)
            for item in self.options:
                if isinstance(item,Menu):
                    html += item.getHTML()

            if self.parent==None:
                html = '\n<div id="menudropdown">\n<dl>%s\n</dl>\n</div>' % html
            elif self.parent.parent:
                html = '\n<dd><dl>%s\n</dl></dd>' % html
        return html


class Tab:
    def __init__(self, items):
        if items:
            bar = '<div id="TabContents">\n<ul>'
            keys = items.keys()
            keys.sort()
            for k in keys:
                v = items[k]
                bar += ('\n<li><a href="#%s"><span>%s</span></a></li>' % (k, v))
            bar += '\n</ul>\n</div>'
            self.bar = bar
        else:
            self.bar = None
        

class FormBuilder():
    def __init__(self, action, method='post', title=''):
        self.action = action
        self.method = method
        self.title = title
        self.fields = []
        self.buttons = []
        self.message = ""
        
    def asDict(self,options, defaultvalue):
        if options:
            if type(options)==list:
                items= []
                for value in options:
                    if value==defaultvalue: 
                        ischecked='selected="selected"' 
                    else: 
                        ischecked=''
                    items.append({'key': value, 'value': value, 'checked': ischecked})
                return items
            elif type(options)==dict:
                items= []
                for k,v in options.iteritems():
                    if v==defaultvalue: 
                        ischecked='selected="selected"' 
                    else: 
                        ischecked=''
                    items.append({'key': k, 'value': v, 'checked': ischecked})
                return items
            else:
                return None
        return None
    
    def addButton(self, label, btntype='submit'):
        self.buttons.append( {"name": "operation", "value":label, "btntype": btntype} )
        
    def addField(self, id, label=None, value=None, size=50, options={}):
        settings = {'mandatory': False, 
                    'newline': True,
                    'disabled': False,
                    'hidden': False,
                    'values': None}
        settings.update(options)
        if not label: 
            label = id
        if not value:
            value = ''
        if settings['disabled']:
            disabled="disabled"
        else:
            disabled=""
            
        field = {"id": id,
                 "label": label, 
                 "value": value, 
                 "size": size, 
                 "mandatory": settings['mandatory'], 
                 "options": self.asDict(settings['values'], value), 
                 "newline": settings['newline'],
                 "hidden": settings["hidden"],
                 "disabled": disabled, 
                 "divend": "</div>"}
        if not settings['newline']:
            if len(self.fields)>0:
                lastfield = self.fields[len(self.fields)-1]
                lastfield["divend"] = ""
        self.fields.append(field)
    
    def delField(self, id):
        return
    
    def render(self):
        if len(self.buttons)==0:
            self.addButton("Salvar")
            self.addButton("Deletar")
            self.addButton("Novo")
        form = Template(
        """    
<form id="dynform" action="{{frmAction}}" method="{{frmMethod}}">
<fieldset>
    <legend>{{frmTitle}}</legend><p align="right">{{frmMessage}}</p>{% for field in frmFields %}{%if field.hidden %}
    <input type="hidden" size="{{field.size}}" name="{{field.id}}" value="{{field.value}}"/>{%else%}{% if field.newline %}
    <div><label for="{{field.id}}" class="error">{% if field.mandatory %}<em>*</em>{%endif%}{{field.label}}:</label>{% endif %}{%if field.options%}
            <select name="{{field.id}}" id="{{field.id}}" size="1" {{field.disabled}} class="error">
                {% for item in field.options %}
                    <option value="{{item.value}}" {{item.checked}}>{{item.key}}</option>
                {% endfor %}
            </select>{% else %}{% if field.disabled %}<input type="hidden" size="{{field.size}}" name="{{field.id}}" value="{{field.value}}"/>
                <input type="text" size="{{field.size}}" value="{{field.value}}" {{field.disabled}} class="error" />
            {% else %}<input type="text" size="{{field.size}}" name="{{field.id}}" value="{{field.value}}" class="error" />{% endif %}{% endif %}{{field.divend}}{%endif%}
{% endfor %}</fieldset>{% if frmButtons%}
<fieldset>
    <div>{% for button in frmButtons %}<input name="{{button.name}}" type="{{button.btntype}}" value="{{button.value}}" class="btnupdate"/>{% endfor %}</div>
 </fieldset>{% endif %}
</form>
{% if fieldfocusid%}
<script type="text/javascript" language="JavaScript">
document.forms['dynform'].elements['{{fieldfocusid}}'].focus();
</script>
{% endif %}
        """)
        fieldfocusid=None;
        for field in self.fields:
            if (not field["options"]) and  (not field["disabled"]) and (not field["hidden"]):
                fieldfocusid=field["id"]
                break
        
        vars = Context({
            "frmAction": self.action,
            "frmMethod": self.method,
            "frmTitle": self.title,
            "frmFields": self.fields,
            "frmButtons": self.buttons, 
            "frmMessage": self.message,
            "fieldfocusid": fieldfocusid
        })
        return form.render(vars)

class Enviroment(webapp.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/html'
        self.response.out.write(
            """<!DOCTYPE html><html><head>
            <title>Authentication</title>
            </head><body>""") 
        for name in os.environ.keys():
            self.response.out.write("%s = %s<br>\n" % (name, os.environ[name]))
        self.response.out.write("</body></html>")

class Render():       
    def __init__(self, request, templatepath, template_values = None):     
        self.request = request        
        if template_values:
            self.template_values = template_values
        else:
            self.template_values = {}
        # Process the given template name
        #path = os.path.dirname(__file__).replace('jail','')
        path=os.path.normpath(os.path.join(os.path.dirname(__file__),os.path.pardir))
        if type(templatepath).__name__=='str':
            if templatepath.endswith(".html") or templatepath.endswith(".htm"): 
                if templatepath.find('/')>=0:
                    templatepath = templatepath.split('/')
                elif templatepath.find('\\')>=0:
                    templatepath = templatepath.split('\\')
                else:
                    templatepath = templatepath.split(' ')
        if type(templatepath).__name__=='list':
            for item in templatepath:
                if item!='':
                    path = os.path.join(path,item)
        else:
            path = templatepath        
        self.path = path
    
    def getReponse(self):   
        if self.path.endswith(".html") or self.path.endswith(".htm"): 
            return template.render(self.path, self.template_values)
        else:
            return template.Template(self.path).render(self.template_values)

    def write(self):
        self.request.response.out.write(unicode(self.getReponse()))

class Handler(webapp.RequestHandler):
    target = None
    def initialize(self, request, response):
        super(Handler,self).initialize(request, response)
        myself = dir(self)
        if not 'templatePath' in myself:
            self.templatePath = None
        if not 'templateData' in myself:
            self.templateData = None
        if not 'localuser' in myself:
            self.localuser    = None        
        if not 'requirelogin' in myself:
            self.requirelogin = False

    def readUser(self):
        self.localuser = None
        usr = users.GetCurrentUser()
        newusr = self.newLocalUser()                            
        if usr:
            gSQL = ("SELECT * FROM %s WHERE user = :1" % newusr.__class__.__name__) 
            all = db.GqlQuery(gSQL, usr)
            self.localuser = all.get()
            if not self.localuser:
                self.localuser = newusr
                self.localuser.user = usr
                self.localuser.put()
        else:
            self.localuser = newusr
            
    def newLocalUser(self):
        return LocalUser()
    
    def get(self):
        if self.login():
            self._get()

    def post(self):
        if self.login():
            self._post()
    
    def _post(self):
        pass
    
    def _get(self):
        pass
    
    def getValue(self,fieldName, default=None):
        ret = self.request.get(fieldName)
        if (len(ret)==0):
            ret = self.request.get(fieldName.upper())
            if (len(ret)==0):
                return default                
        return cgi.escape(ret)
    
    def login(self):
        if self.localuser:
            if not self.localuser.user:
                self.redirect(self.urlLogin())
                return False
            else:
                return True
        
    def urlLogin(self):
        return users.CreateLoginURL(self.request.uri)
    
    def urlLogout(self):
        return users.CreateLogoutURL(self.request.uri)
    
    def getUsrSign(self):
        if self.localuser:
            if self.localuser.user:         
                return '<a href="' + self.urlLogout() + '">Logout</a>'
            else:
                return '<a href="' + self.urlLogin() + '">Login</a>'
        else:
            return ''
        
    def go(self,to):
        self.redirect(to)
        
    def goRoot(self):
        self.go('/')
    
    def goIndex(self):
        self.go('/index')
    
    def goHome(self):
        self.go(self.request.uri)

    def refresh(self):
        self.go(self.request.uri)
    
    def json(self,data):
        return simplejson.dumps(data)
    
    def render(self, templatePath=None,templateData=None):
        """ Render a django template or a write json content.
        
        templatePath - HTML file to be used
        tempalteData - Data to be replaced (if templatePath is given) or Dict to be 
                       rendered as json object
        """
        if not templatePath and self.templatePath:
            templatePath=self.templatePath
                    
        if not templateData and self.templateData:
            templateData=self.templateData       
        
        if templateData:
            if templatePath and not templatePath=='json':
                if (self.requirelogin):
                    usrSignUrl = ''
                    if ('getUsrSign' in dir(self)):
                        usrSignUrl = self.getUsrSign()                
                    defaultValues = {'usrsign': usrSignUrl}
                    templateData.update(defaultValues)            
                render = Render(self, templatePath, templateData) 
                try:
                    render.write()
                except:
                    self.usererror("Template %s does not exist" % (render.path))
            else:
                self.response.out.write(self.json(templateData))
        else:
            self.usererror("Template not defined")
            
    def usererror(self,msg):
        self.response.headers['Content-Type'] = 'text/html'
        self.response.out.write(
            """<!DOCTYPE html><html><head>
            <title>Authentication</title>
            </head><body>""")
        self.response.out.write(msg)
        self.response.out.write("</body></html>")
        
    def setSessionData(self,varname,vardata):
        session = self.session
        session[varname] = vardata
        session.save()

    def getSessionData(self,varname,defaultvalue=None):
        session = self.session
        if not session.has_key(varname):
            if defaultvalue:
                session[varname] = defaultvalue
                session.save()
                return defaultvalue
            else:
                return None
        else:
            return session[varname]      


#curSession = sessions.Session()
curSession = None
